package com.hmy.finance.service.core.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmy.ccp.common.dto.ContextUserInfo;
import com.hmy.ccp.common.response.BasePageResponse;
import com.hmy.ccp.error.exception.HMYRuntimeException;
import com.hmy.ccp.starter.web.utils.UserContextUtil;
import com.hmy.finance.api.bo.core.request.enums.FeeTypeEnum;
import com.hmy.finance.bo.request.ReceivablesQueryRequest;
import com.hmy.finance.bo.response.ReceivablesExportResponse;
import com.hmy.finance.bo.response.ReceivablesQueryResponse;
import com.hmy.finance.common.constants.CommonConstants;
import com.hmy.finance.common.easyexcel.SimpleExcelHandler;
import com.hmy.finance.common.easyexcel.pojo.exportbeans.ExportSingleData;
import com.hmy.finance.common.easyexcel.pojo.exportbeans.ExportSingleSheetBaseContext;
import com.hmy.finance.common.enums.BusinessBillTypeEnum;
import com.hmy.finance.common.properties.ReceivableInventoryOverdueRule;
import com.hmy.finance.dal.dos.ContractTotalReceivableDo;
import com.hmy.finance.dal.mapper.DeviceDailyBillMapper;
import com.hmy.finance.dal.mapper.DeviceRentalRecordMapper;
import com.hmy.finance.dal.mapper.ProfitLossRecordMapper;
import com.hmy.finance.dal.mapper.ReceivableInventoryMapper;
import com.hmy.finance.dal.po.DeviceDailyBillPo;
import com.hmy.finance.dal.po.DeviceRentalRecordPo;
import com.hmy.finance.dal.po.ProfitLossRecordPo;
import com.hmy.finance.dal.po.ReceivableInventoryPo;
import com.hmy.finance.dal.po.json.OverdueDetailJson;
import com.hmy.finance.manager.uua.DeptClientRpc;
import com.hmy.finance.manager.uua.UserFeignClientRpc;
import com.hmy.finance.service.core.IReceivableInventoryService;
import com.hmy.finance.service.core.dto.DailyInvoiceGenerationJobDto;
import com.hmy.finance.service.core.dto.OverdueDetailDto;
import com.hmy.finance.service.core.dto.ReceivablesQueryDto;
import com.hmy.finance.service.rdto.BasePageResultRdto;
import com.hmy.finance.service.rdto.ContractTotalReceivableRdto;
import com.hmy.finance.service.rdto.ReceivableInventoryExportRdto;
import com.hmy.finance.service.rdto.ReceivableInventoryRdto;
import com.hmy.fulfillment.api.common.enums.RoleEnum;
import com.hmy.order.api.common.enums.OrderPaymentMethodEnum;
import com.hmy.order.api.common.enums.OrderPaymentPeriodEnum;
import com.hmy.uaa.api.bo.request.QueryUserInfoRequest;
import com.hmy.uaa.api.bo.response.QueryDepartmentResponse;
import com.hmy.uaa.api.bo.response.user.UserInfoResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author songkf
 * @description 针对表【receivable_Inventory(应收盘点)】的数据库操作Service实现
 * @createDate 2024-02-21 18:55:01
 */

@Slf4j
@Service
public class ReceivableInventoryServiceImpl implements IReceivableInventoryService {

    @Resource
    private ReceivableInventoryMapper receivableInventoryMapper;

    @Resource
    private ProfitLossRecordMapper profitLossRecordMapper;
    @Resource
    private SimpleExcelHandler simpleExcelHandler;

    /**
     * 应收盘点逾期规则
     */
    @Resource
    private ReceivableInventoryOverdueRule receivableInventoryOverdueRule;

    /**
     * 设备日流水mapper
     */
    @Resource
    private DeviceDailyBillMapper deviceDailyBillMapper;
    @Resource
    private DeviceRentalRecordMapper deviceRentalRecordMapper;


    @Resource
    private DeptClientRpc deptClientRpc;

    @Resource
    private UserFeignClientRpc userFeignClientRpc;

    @Override
    public BasePageResponse<ReceivablesQueryResponse> queryReceivablesByPage(ReceivablesQueryRequest receivablesQueryRequest) {
        ReceivablesQueryDto receivablesQueryDto = BeanUtil.copyProperties(receivablesQueryRequest, ReceivablesQueryDto.class);
        /*根据登录用户的角色，设置查询条件*/
        setUserInfoContextInfo(receivablesQueryDto);
        log.info("IReceivableInventoryService#queryReceivablesByPage param:{}", JSON.toJSON(receivablesQueryDto));

        if (CollectionUtil.isEmpty(receivablesQueryDto.getRoles())) {
            throw new HMYRuntimeException(506100002);
        }

        /*查询条件组合*/
        LambdaQueryWrapper<ReceivableInventoryPo> queryWrapper = new LambdaQueryWrapper<>();
        Boolean hasPermissions = packageQueryWrapper(receivablesQueryDto, queryWrapper);

        BasePageResultRdto<ReceivableInventoryRdto> pageResultRdto = new BasePageResultRdto<>();
        if (hasPermissions) {
            pageResultRdto = selectOrderPrimaryRecords(receivablesQueryDto.getPageNum(), receivablesQueryDto.getPageSize(), queryWrapper);
        }

        /*封装为ReceivablesQueryResponse*/
        BasePageResponse<ReceivablesQueryResponse> response = new BasePageResponse<>();
        response.setRecords(BeanUtil.copyToList(pageResultRdto.getRecords(), ReceivablesQueryResponse.class));
        response.setTotal(pageResultRdto.getTotal());
        response.setPages(pageResultRdto.getPages());
        response.setSize(pageResultRdto.getSize());
        response.setCurrent(pageResultRdto.getCurrent());
        log.info("IReceivableInventoryService#queryReceivablesByPage result:{}", JSON.toJSON(response));
        return response;
    }

    @Override
    public ReceivablesExportResponse exportReceivablesByParams(ReceivablesQueryRequest receivablesExportRequest) {
        ReceivablesExportResponse response = new ReceivablesExportResponse();
        ReceivablesQueryDto receivablesQueryDto = BeanUtil.copyProperties(receivablesExportRequest, ReceivablesQueryDto.class);

        /*根据登录用户的角色，设置查询条件*/
        setUserInfoContextInfo(receivablesQueryDto);

        if (CollectionUtil.isEmpty(receivablesQueryDto.getRoles())) {
            throw new HMYRuntimeException("用户角色为空");
        }

        /*查询条件组合*/
        LambdaQueryWrapper<ReceivableInventoryPo> queryWrapper = new LambdaQueryWrapper<>();
        Boolean hasPermissions = packageQueryWrapper(receivablesQueryDto, queryWrapper);
        List<ReceivableInventoryPo> receivableInventoryRos = new ArrayList<>();
        if (hasPermissions) {
            receivableInventoryRos = receivableInventoryMapper.selectList(queryWrapper);
        }

        if (CollectionUtil.isEmpty(receivableInventoryRos)) {
            response.setOssUrl("");
            response.setErrorMessage("没有查询到数据");
            return response;
        }

        List<ReceivableInventoryRdto> receivableInventoryRdtos;
        receivableInventoryRdtos = BeanUtil.copyToList(receivableInventoryRos, ReceivableInventoryRdto.class);//先转rdto,调用公共方法，图省事。
        packageReceivableInventoryRdtoHandler(receivableInventoryRdtos);
        List<ReceivableInventoryExportRdto> exportRecords = BeanUtil.copyToList(receivableInventoryRdtos, ReceivableInventoryExportRdto.class);

        LocalDateTime deadTime = LocalDateTime.of(LocalDateTime.now().minusDays(1).toLocalDate(), LocalTime.MAX);
        String deadTimeStr = deadTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        /*查询区域信息*/
        List<Long> departIds = exportRecords.stream().map(ReceivableInventoryExportRdto::getSaleManagerDeptId).collect(Collectors.toList());
        List<QueryDepartmentResponse> departmentResponses = deptClientRpc.getDeptInfoByIds(departIds);
        exportRecords.forEach(o -> {
            departmentResponses.stream().filter(d -> d.getId().equals(o.getSaleManagerDeptId())).findFirst().ifPresent(d -> {
                o.setAreaName(d.getName());
            });
            o.setDeadTime(deadTimeStr);
            o.getOverdueDetail().sort(Comparator.comparing(OverdueDetailJson::getOverdueDaysStart));
            o.setOverdueAmount30(CollectionUtil.isNotEmpty(o.getOverdueDetail()) ? o.getOverdueDetail().get(0).getOverdueAmount() : BigDecimal.ZERO);
            o.setOverdueAmount60(CollectionUtil.isNotEmpty(o.getOverdueDetail()) ? o.getOverdueDetail().get(1).getOverdueAmount() : BigDecimal.ZERO);
            o.setOverdueAmount90(CollectionUtil.isNotEmpty(o.getOverdueDetail()) ? o.getOverdueDetail().get(2).getOverdueAmount() : BigDecimal.ZERO);
            o.setOverdueAmount91(CollectionUtil.isNotEmpty(o.getOverdueDetail()) ? o.getOverdueDetail().get(3).getOverdueAmount() : BigDecimal.ZERO);
        });

        /*上传oss*/
        ExportSingleSheetBaseContext<List<? extends ExportSingleData>> writeObj = new ExportSingleSheetBaseContext<>();
        writeObj.setData(exportRecords);
        writeObj.setClazz(ReceivableInventoryExportRdto.class);
        writeObj.setFileName("应收盘点导出" + LocalDateTime.now());
        String ossUrl = simpleExcelHandler.excelExportHandler(writeObj);
        if (StringUtils.isEmpty(ossUrl)) {
            response.setErrorMessage("数据导出失败");
        }
        response.setOssUrl(ossUrl);
        return response;
    }

    @Override
    public Boolean handleReceivableInventory(DailyInvoiceGenerationJobDto dto) {
        log.info("[处理时间:{}]-[合同编号:{}]-开始处理应收盘点", DateUtil.now(), dto.getContractExtensionInfoJson().getContractNo());
        ReceivableInventoryPo receivableInventoryPo = this.receivableInventoryMapper.selectOne(new LambdaQueryWrapper<ReceivableInventoryPo>().eq(ReceivableInventoryPo::getContractNo, dto.getContractExtensionInfoJson().getContractNo()));
        log.info("[处理时间:{}]-[合同编号:{}]-查询应收盘点结果:{}", DateUtil.now(), dto.getContractExtensionInfoJson().getContractNo(), receivableInventoryPo);
        if (Objects.isNull(receivableInventoryPo)) {
            log.info("[处理时间:{}]-[合同编号:{}]-未查询到应收盘点,开始创建应收盘点", DateUtil.now(), dto.getContractExtensionInfoJson().getContractNo());
            //创建应收盘点
            receivableInventoryPo = initializationReceivableInventory(dto);
        }
        //查询本合同下的所有设备的日流水数据
        List<DeviceDailyBillPo> deviceDailyBillList = deviceDailyBillMapper.selectList(Wrappers.lambdaQuery(DeviceDailyBillPo.class).eq(DeviceDailyBillPo::getContractId, dto.getContractExtensionInfoJson().getContractId()));
        dto.setCalculateContractReceivableInventoryDeviceDailyBillList(deviceDailyBillList);
        //设置总营收等数据
        receivableInventoryPo.setTotalRevenueAmount(dto.getContractDailyBillPo().getTotalRevenueAmount());
        //设置TotalReconciliationedAmount需取dto.getContractDailyBillPo()中的TotalReconciliationedAmount与HistoryReconciledAmount的最大值
        receivableInventoryPo.setTotalReconciliationedAmount(dto.getContractDailyBillPo().getTotalReconciledAmount().max(dto.getContractDailyBillPo().getHistoryReconciledAmount()));
        //设置TotalUnreconciliationAmount=TotalRevenueAmount-TotalReconciliationedAmount
        receivableInventoryPo.setTotalUnreconciliationAmount(receivableInventoryPo.getTotalRevenueAmount().subtract(receivableInventoryPo.getTotalReconciliationedAmount()));
        //
        receivableInventoryPo.setTotalReceivedAmount(deviceDailyBillList.stream().map(DeviceDailyBillPo::getPaidAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        receivableInventoryPo.setTotalReceivableAmount(receivableInventoryPo.getTotalReceivedAmount().add(profitLossRecordMapper.selectList(Wrappers.lambdaQuery(ProfitLossRecordPo.class).eq(ProfitLossRecordPo::getContractId, dto.getContractExtensionInfoJson().getContractId()).eq(ProfitLossRecordPo::getBusinessBillType, BusinessBillTypeEnum.RECEIVABLE_ADJUSTMENT)).stream().map(ProfitLossRecordPo::getPaidAmount).reduce(BigDecimal.ZERO, BigDecimal::add)));
        //应收总额=总营收-总已收
        receivableInventoryPo.setTotalReceivableAmount(receivableInventoryPo.getTotalRevenueAmount().subtract(receivableInventoryPo.getTotalReceivedAmount()));
        List<DeviceRentalRecordPo> deviceRentalRecordList = deviceRentalRecordMapper.selectList(Wrappers.<DeviceRentalRecordPo>lambdaQuery().eq(DeviceRentalRecordPo::getContractId, dto.getContractExtensionInfoJson().getContractId()));

        calculateReceivableInventoryPeriodAmount(receivableInventoryPo, deviceRentalRecordList);
        int result = 0;
        //更新或插入应收盘点
        if (receivableInventoryPo.getId() == null) {
            result = receivableInventoryMapper.insert(receivableInventoryPo);
        } else {
            result = receivableInventoryMapper.updateById(receivableInventoryPo);
        }
        return result > 0;
    }

    @Override
    public List<ReceivableInventoryPo> selectWaitOffsetList(Long customerId, Long contractId, LocalDate startDate, LocalDate endDate) {
        LambdaQueryWrapper<ReceivableInventoryPo> queryWrapper = new LambdaQueryWrapper<>();
        //only search total receivable amount greater than zero
        queryWrapper.gt(ReceivableInventoryPo::getTotalReceivableAmount, BigDecimal.ZERO);
        if (Objects.nonNull(customerId)) {
            queryWrapper.eq(ReceivableInventoryPo::getCustomerId, customerId);
        }
        if (Objects.nonNull(contractId)) {
            queryWrapper.eq(ReceivableInventoryPo::getContractId, contractId);
        }

        //        .ge(Objects.nonNull(startDate), ReceivableInventoryPo::getOffsetThroughDate, startDate)
        //      .le(Objects.nonNull(endDate), ReceivableInventoryPo::getOffsetThroughDate, endDate);
        if (Objects.nonNull(startDate)) {
            queryWrapper.ge(ReceivableInventoryPo::getOffsetThroughDate, startDate);
        }
        if (Objects.nonNull(endDate)) {
            queryWrapper.and(wra -> wra.isNull(ReceivableInventoryPo::getOffsetThroughDate).or().le(ReceivableInventoryPo::getOffsetThroughDate, endDate));
        }
        return this.receivableInventoryMapper.selectList(queryWrapper);
    }

    @Override
    public List<ContractTotalReceivableRdto> queryContractTotalReceivableAmountByContractIds(List<Long> contractIds) {
        List<ContractTotalReceivableRdto> receivableRdtos;
        log.info("iReceivableInventoryService#queryContractTotalReceivableAmountByContractIds request:{}", JSON.toJSON(contractIds));
        List<ContractTotalReceivableDo> contractTotalReceivables = receivableInventoryMapper.selectContractTotalReceivableAmount(contractIds);
        receivableRdtos = BeanUtil.copyToList(contractTotalReceivables, ContractTotalReceivableRdto.class);
        log.info("iReceivableInventoryService#queryContractTotalReceivableAmountByContractIds response:{}", JSON.toJSON(receivableRdtos));
        return receivableRdtos;
    }

    @Override
    public Boolean updateOffsetThroughDate(Long id, LocalDate localDate) {
        ReceivableInventoryPo receivableInventoryPo = new ReceivableInventoryPo();
        receivableInventoryPo.setId(id);
        receivableInventoryPo.setOffsetThroughDate(localDate);
        return receivableInventoryMapper.updateById(receivableInventoryPo) >= 0;
    }

    @Override
    public ReceivableInventoryPo getReceivableInventoryByContractId(Long contractId) {
        return this.receivableInventoryMapper.selectOne(new LambdaQueryWrapper<ReceivableInventoryPo>().eq(ReceivableInventoryPo::getContractNo, contractId));
    }


    private Boolean packageQueryWrapper(ReceivablesQueryDto receivablesQueryDto, LambdaQueryWrapper<ReceivableInventoryPo> queryWrapper) {
        Boolean hasPermissions = Boolean.TRUE;
        if (receivablesQueryDto.getRoles().contains(RoleEnum.SALE_MANAGER.getRoleType())) {
            //如果是销售经理，则只查询他的数据
            queryWrapper.eq(ReceivableInventoryPo::getSaleManagerId, receivablesQueryDto.getRoleSaleManagerId());//销售经理id
            if (StringUtils.isNotEmpty(receivablesQueryDto.getAreaCode())) {
                queryWrapper.eq(ReceivableInventoryPo::getSaleManagerDeptId, receivablesQueryDto.getAreaCode());
            }
        } else if (receivablesQueryDto.getRoles().contains(RoleEnum.AREA_MANAGER.getRoleType()) || receivablesQueryDto.getRoles().contains(RoleEnum.CLERK.getRoleType())) {
            //如果是区域经理或者门店行政，且前端没有指定，则则查询他归属部门下的数据(销售经理的所在的部门在区域经理的部门或下属部门)
            if (Objects.nonNull(receivablesQueryDto.getSaleManagerId())) {
                queryWrapper.in(ReceivableInventoryPo::getSaleManagerId, receivablesQueryDto.getSaleManagerId());
            }
            if (StringUtils.isNotEmpty(receivablesQueryDto.getAreaCode())) {
                if (receivablesQueryDto.getDeptIds().contains(Long.valueOf(receivablesQueryDto.getAreaCode()))) {
                    //注意，此处的设置和上面具有顺序性，如果有区域经理没有选择区域，则默认查他下部门所有的，如果有区域经理选择了区域，则查询此区域
                    queryWrapper.eq(ReceivableInventoryPo::getSaleManagerDeptId, receivablesQueryDto.getAreaCode());
                } else {
                    hasPermissions = Boolean.FALSE;
                }
            } else {
                queryWrapper.in(ReceivableInventoryPo::getSaleManagerDeptId, receivablesQueryDto.getDeptIds());//限制只能查自己部门下面的数据
            }
        } else {
            //如果是管理员或者其他角色，且前端没有指定，则查所有数据
            if (Objects.nonNull(receivablesQueryDto.getSaleManagerId())) {
                queryWrapper.in(ReceivableInventoryPo::getSaleManagerId, receivablesQueryDto.getSaleManagerId());
            }
            if (StringUtils.isNotEmpty(receivablesQueryDto.getAreaCode())) {
                //注意，此处的设置和上面具有顺序性，如果有区域经理没有选择区域，则默认查他下部门所有的，如果有区域经理选择了区域，则查询此区域
                queryWrapper.eq(ReceivableInventoryPo::getSaleManagerDeptId, receivablesQueryDto.getAreaCode());
            }
        }

        if (StringUtils.isNotEmpty(receivablesQueryDto.getLesseeFirst())) {
            queryWrapper.like(ReceivableInventoryPo::getLesseeFirst, receivablesQueryDto.getLesseeFirst());//承租方1
        }
        if (StringUtils.isNotEmpty(receivablesQueryDto.getLesseeSecond())) {
            queryWrapper.like(ReceivableInventoryPo::getLesseeSecond, receivablesQueryDto.getLesseeSecond());//承租方2
        }
        if (StringUtils.isNotEmpty(receivablesQueryDto.getContractNo())) {
            queryWrapper.like(ReceivableInventoryPo::getContractNo, receivablesQueryDto.getContractNo());//合同编号
        }
        if (Objects.nonNull(receivablesQueryDto.getContractSignTimeStart())) {
            queryWrapper.ge(ReceivableInventoryPo::getContractSignTime, receivablesQueryDto.getContractSignTimeStart().withHour(00).withMinute(00).withSecond(00));//大于等于合同签署时间start
        }
        if (Objects.nonNull(receivablesQueryDto.getContractSignTimeEnd())) {
            queryWrapper.le(ReceivableInventoryPo::getContractSignTime, receivablesQueryDto.getContractSignTimeEnd().withHour(23).withMinute(59).withSecond(59));//小于等于合同签署时间end
        }
        return hasPermissions;
    }


    private BasePageResultRdto<ReceivableInventoryRdto> selectOrderPrimaryRecords(Integer pageNum, Integer pageSize, LambdaQueryWrapper<ReceivableInventoryPo> queryWrapper) {
        Page<ReceivableInventoryPo> page = new Page<>(pageNum, pageSize);
        queryWrapper.orderByDesc(ReceivableInventoryPo::getCreateTime);//按创建时间倒序排序

        Page<ReceivableInventoryPo> pageResult = receivableInventoryMapper.selectPage(page, queryWrapper);
        /*封装结果集*/
        List<ReceivableInventoryRdto> receivableInventoryRdtos = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(pageResult.getRecords())) {
            receivableInventoryRdtos = BeanUtil.copyToList(pageResult.getRecords(), ReceivableInventoryRdto.class);
            packageReceivableInventoryRdtoHandler(receivableInventoryRdtos);
            /*计算逾期情况(1-30,31-60,61-90)*/
            //对于orderPrimarys.getOverdueDetail()取到的list,根据overdueDaysStart进行从小到大排序，并从1开始自增对sort赋值，如overdueDaysStart最小则对应的sort设置为1,以此类推。
            receivableInventoryRdtos.forEach(o -> {
                if (CollectionUtil.isNotEmpty(o.getOverdueDetail())) {
                    o.getOverdueDetail().sort(Comparator.comparing(OverdueDetailDto::getOverdueDaysStart));
                    o.getOverdueDetail().forEach(overdue -> {
                        overdue.setSort(o.getOverdueDetail().indexOf(overdue) + 1);
                        if (overdue.getOverdueDaysStart() == 91) {
                            overdue.setOverdueDaysDesc("逾期" + overdue.getOverdueDaysStart() + "天以上金额 (元)");
                        } else {
                            overdue.setOverdueDaysDesc("逾期" + overdue.getOverdueDaysStart() + "-" + overdue.getOverdueDaysEnd() + "天内金额 (元)");
                        }
                    });
                }
            });
        }
        BasePageResultRdto<ReceivableInventoryRdto> basePageResult = new BasePageResultRdto<>();
        basePageResult.setRecords(receivableInventoryRdtos);
        basePageResult.setTotal(pageResult.getTotal());
        basePageResult.setPages(pageResult.getPages());
        basePageResult.setSize(pageResult.getSize());
        basePageResult.setCurrent(pageResult.getCurrent());
        return basePageResult;
    }

    private void packageReceivableInventoryRdtoHandler(List<ReceivableInventoryRdto> receivableInventoryRdtos) {
        /*查询区域信息*/
        List<Long> departIds = receivableInventoryRdtos.stream().map(ReceivableInventoryRdto::getSaleManagerDeptId).collect(Collectors.toList());
        QueryUserInfoRequest request = new QueryUserInfoRequest();
        request.setRoleKey(RoleEnum.AREA_MANAGER.getRoleType());
        request.setDeptIds(departIds);
        List<UserInfoResponse> userInfoList = userFeignClientRpc.getUserInfoList(request);
        List<QueryDepartmentResponse> departmentResponses = deptClientRpc.getDeptInfoByIds(departIds);
        receivableInventoryRdtos.forEach(o -> {
            //补足区域信息
            departmentResponses.stream().filter(d -> d.getId().equals(o.getSaleManagerDeptId())).findFirst().ifPresent(d -> {
                o.setAreaName(d.getName());
            });
            //补足区域经理信息
            userInfoList.stream().filter(d -> o.getSaleManagerDeptId().toString().equals(d.getDeptId())).findFirst().ifPresent(d -> {
                o.setAreaManagerId(d.getUserId());
                o.setAreaManagerName(d.getUserName());
            });


            if (OrderPaymentPeriodEnum.PAYMENT_PERIOD_PRE_PAYMENT.getCode().equals(o.getOrderExtensionInfo().getAccountPeriod())) {
                //先付是-，后付是+
                o.setAccountPeriod(OrderPaymentPeriodEnum.PAYMENT_PERIOD_PRE_PAYMENT.getDesc());
            } else {
                String symbol = "+";
                o.setAccountPeriod(OrderPaymentMethodEnum.getDescByType(o.getOrderExtensionInfo().getSettlementMethod()) + symbol + o.getOrderExtensionInfo().getAccountPeriodDays());
            }

            o.setSettlementMethodDesc(OrderPaymentMethodEnum.getDescByType(o.getOrderExtensionInfo().getSettlementMethod()));
        });
    }


    /**
     * 初始化应收盘点
     *
     * @param dto 每日流水处理任务数据传输对象
     * @return 应收盘点
     * @author songkf
     */
    public static ReceivableInventoryPo initializationReceivableInventory(DailyInvoiceGenerationJobDto dto) {
        ReceivableInventoryPo receivableInventoryPo = new ReceivableInventoryPo();
        receivableInventoryPo.setCustomerId(dto.getCustomerExtensionInfoJson().getCustomerId());
        receivableInventoryPo.setCustomerName(dto.getCustomerExtensionInfoJson().getCustomerName());
        receivableInventoryPo.setSignatoryId(dto.getCustomerExtensionInfoJson().getSignatoryId());
        receivableInventoryPo.setLesseeFirst(dto.getCustomerExtensionInfoJson().getSignatoryName());
        receivableInventoryPo.setLesseeSecond(dto.getCustomerExtensionInfoJson().getCustomerName());
        receivableInventoryPo.setContractId(dto.getContractExtensionInfoJson().getContractId());
        receivableInventoryPo.setContractNo(dto.getContractExtensionInfoJson().getContractNo());
        receivableInventoryPo.setOrderId(dto.getOrderExtensionInfoJson().getOrderId());
        receivableInventoryPo.setTotalReceivableAmount(BigDecimal.ZERO);
        receivableInventoryPo.setTotalReceivedAmount(BigDecimal.ZERO);
        receivableInventoryPo.setTotalInvoicedAmount(BigDecimal.ZERO);
        receivableInventoryPo.setTotalReconciliationedAmount(BigDecimal.ZERO);
        receivableInventoryPo.setTotalUnreconciliationAmount(BigDecimal.ZERO);
        receivableInventoryPo.setTotalOverdueAmount(BigDecimal.ZERO);
        receivableInventoryPo.setUnexpiredAmount(BigDecimal.ZERO);
        receivableInventoryPo.setCurrentAmount(BigDecimal.ZERO);
        receivableInventoryPo.setSaleManagerId(dto.getOrderExtensionInfoJson().getSaleManagerId());
        receivableInventoryPo.setSaleManagerName(dto.getOrderExtensionInfoJson().getSaleManagerName());
        receivableInventoryPo.setSaleManagerDeptId(dto.getOrderExtensionInfoJson().getSaleManagerDeptId());
//        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS");
        receivableInventoryPo.setContractSignTime(DateUtil.parse(dto.getContractExtensionInfoJson().getContractSignTime()).toLocalDateTime());
        receivableInventoryPo.setContractExtensionInfo(dto.getContractExtensionInfoJson());
        receivableInventoryPo.setOrderExtensionInfo(dto.getOrderExtensionInfoJson());
        receivableInventoryPo.setCustomerExtensionInfo(dto.getCustomerExtensionInfoJson());
        receivableInventoryPo.setOverdueDetail(new ArrayList<>());
        receivableInventoryPo.setCreateTime(LocalDateTime.now());
        receivableInventoryPo.setUpdateTime(LocalDateTime.now());
        receivableInventoryPo.setCreatorName("system");
        receivableInventoryPo.setCreatorId(-1L);
        receivableInventoryPo.setUpdaterName("system");
        receivableInventoryPo.setUpdaterId(-1L);
        receivableInventoryPo.setIsDeleted(CommonConstants.NOT_DELETED);
        return receivableInventoryPo;
    }

    /**
     * 计算应收盘点账期金额
     *
     * @param receivableInventoryPo 应收盘点
     * @param dto                   每日流水处理任务数据传输对象
     * @author songkf
     */
    @Override
    public void calculateReceivableInventoryPeriodAmount(ReceivableInventoryPo receivableInventoryPo, List<DeviceRentalRecordPo> deviceRentalRecordList) {
        //遍历dto的设备租赁记录，基于设备租赁记录中的账龄信息统计出应收盘点的逾期信息
        receivableInventoryPo.setCurrentAmount(BigDecimal.ZERO);
        receivableInventoryPo.setUnexpiredAmount(BigDecimal.ZERO);
        receivableInventoryPo.setTotalOverdueAmount(BigDecimal.ZERO);
        List<OverdueDetailJson> inventoryOverdueList = new ArrayList<>();
        //已生效的应收调整记录ID
        List<Long> profitLossRecordIds = new ArrayList<>();
        deviceRentalRecordList.forEach(deviceRentalRecordPo -> {
            //计算设备租赁记录的账期金额
            deviceRentalRecordPo.getOverdueInfoList().forEach(item -> {
                //查询费用发生日在item.getOverdueDaysStart()和item.getOverdueDaysEnd()之间的ProfitLossRecordPo中的应收调整业务类型的记录
                //如果dto.getHandleDate等于今天
                BigDecimal adjustAmount = BigDecimal.ZERO;
                //如果item.getOverdueEndDate()不为Null
                if (item.getOverdueEndDate() != null) {
                    //计算出时间区间
                    List<ProfitLossRecordPo> profitLossRecordList = profitLossRecordMapper.selectList(Wrappers.<ProfitLossRecordPo>lambdaQuery().eq(ProfitLossRecordPo::getContractId, receivableInventoryPo.getContractId()).eq(ProfitLossRecordPo::getBusinessBillType, BusinessBillTypeEnum.RECEIVABLE_ADJUSTMENT).ge(item.getOverdueStartDate() != null, ProfitLossRecordPo::getFeeIncurredTime, item.getOverdueStartDate() != null ? item.getOverdueStartDate().atStartOfDay() : null).le(item.getOverdueEndDate() != null, ProfitLossRecordPo::getFeeIncurredTime, item.getOverdueEndDate() != null ? item.getOverdueEndDate().atTime(LocalTime.MAX) : null).eq(ProfitLossRecordPo::getIsDeleted, CommonConstants.NOT_DELETED));
                    if (CollectionUtil.isNotEmpty(profitLossRecordList)) {
                        //遍历profitLossRecordList，如果FeeType是减免类型，则减去，否则加上
                        adjustAmount = profitLossRecordList.stream().map(i -> {
                            if (profitLossRecordIds.contains(i.getId())) {
                                return BigDecimal.ZERO;
                            }
                            profitLossRecordIds.add(i.getId());
                            if (Arrays.asList(FeeTypeEnum.RENT_FEE_WAIVER, FeeTypeEnum.PAUSE_FEE_WAIVER, FeeTypeEnum.SHIPPING_FEE_WAIVER, FeeTypeEnum.MODIFICATION_FEE_WAIVER, FeeTypeEnum.CLAIMS_FEE_WAIVER).contains(i.getFeeType())) {
                                return i.getTotalFeeAmount().negate();
                            }
                            return i.getTotalFeeAmount();
                        }).reduce(BigDecimal.ZERO, BigDecimal::add);
                    }

                }


                BigDecimal overdueAmount = item.getOverdueAmount().add(adjustAmount);
                //如果item.getOverdueDaysDesc()==CommonConstants.UNEXPIRED_PERIOD,则将item.getOverdueAmount()加到receivableInventoryPo.getUnexpiredAmount()上
                //如果item.getOverdueDaysDesc()==CommonConstants.CURRENT_PERIOD,则将item.getOverdueAmount()加到receivableInventoryPo.getCurrentAmount()上
                //否则，判断inventoryOverdueList中是否存在overdueDaysDesc==item.getOverdueDaysDesc()的记录，如果存在则将item.getOverdueAmount()加到该记录的overdueAmount上，否则新增一条记录
                if (CommonConstants.UNEXPIRED_PERIOD.equals(item.getOverdueDaysDesc())) {
                    receivableInventoryPo.setUnexpiredAmount(receivableInventoryPo.getUnexpiredAmount().add(overdueAmount));
                } else if (CommonConstants.CURRENT_PERIOD.equals(item.getOverdueDaysDesc())) {
                    receivableInventoryPo.setCurrentAmount(receivableInventoryPo.getCurrentAmount().add(overdueAmount));
                } else {
                    AtomicReference<OverdueDetailJson> overdueDetailJson = new AtomicReference<>(null);
                    for (OverdueDetailJson i : inventoryOverdueList) {
                        if (i.getOverdueDaysDesc().equals(item.getOverdueDaysDesc())) {
                            overdueDetailJson.set(i);
                        }
                    }
                    if (Objects.isNull(overdueDetailJson.get())) {
                        overdueDetailJson.set(new OverdueDetailJson());
                        overdueDetailJson.get().setOverdueDaysKey(item.getOverdueDaysKey());
                        overdueDetailJson.get().setOverdueDaysDesc(item.getOverdueDaysDesc());
                        overdueDetailJson.get().setOverdueDaysStart(item.getOverdueDaysStart());
                        overdueDetailJson.get().setOverdueDaysEnd(item.getOverdueDaysEnd());
                        overdueDetailJson.get().setOverdueAmount(overdueAmount);
                        inventoryOverdueList.add(overdueDetailJson.get());
                    } else {
                        overdueDetailJson.get().setOverdueAmount(overdueDetailJson.get().getOverdueAmount().add(item.getOverdueAmount()));
                    }
                    receivableInventoryPo.setTotalOverdueAmount(receivableInventoryPo.getTotalOverdueAmount().add(overdueAmount));
                }
            });
            receivableInventoryPo.setOverdueDetail(inventoryOverdueList);
        });
    }

    @Override
    public Boolean refreshReceivableInventory(Long contractId, List<DeviceDailyBillPo> allDeviceDailyBillPoList, List<DeviceRentalRecordPo> deviceRentalRecordPoList) {
        ReceivableInventoryPo receivableInventoryPo = this.receivableInventoryMapper.selectOne(new LambdaQueryWrapper<ReceivableInventoryPo>().eq(ReceivableInventoryPo::getContractId, contractId));
        if (Objects.isNull(receivableInventoryPo)) {
            return Boolean.FALSE;
        }
        BigDecimal totalRevenueAmount = BigDecimal.ZERO;
        BigDecimal totalReceivedAmount = BigDecimal.ZERO;
        for (DeviceDailyBillPo deviceDailyBillPo : allDeviceDailyBillPoList) {
            totalRevenueAmount = totalRevenueAmount.add(deviceDailyBillPo.getFee());
            if (deviceDailyBillPo.getTotalProfitLossAmount().compareTo(BigDecimal.ZERO) != 0) {
                totalRevenueAmount = totalRevenueAmount.add(deviceDailyBillPo.getProfitLossInfo().stream().map(i -> {
                    FeeTypeEnum typeEnum = FeeTypeEnum.getFeeTypeEnumByValue(i.getType());
                    log.info("typeEnum:{}", typeEnum);
                    BigDecimal negate = i.getFee();
                    if (Arrays.asList(FeeTypeEnum.INITIAL_DAY_FEE_WAIVER, FeeTypeEnum.SUPPLEMENTARY_PAUSE_FEE, FeeTypeEnum.EXIT_PAUSE_FEE_WAIVER, FeeTypeEnum.RENT_FEE_WAIVER, FeeTypeEnum.PAUSE_FEE_WAIVER, FeeTypeEnum.SHIPPING_FEE_WAIVER, FeeTypeEnum.MODIFICATION_FEE_WAIVER, FeeTypeEnum.CLAIMS_FEE_WAIVER).contains(typeEnum)) {

                        negate = negate.negate();
                    }
                    log.info("negate:{}", negate);
                    return negate;

                }).reduce(BigDecimal.ZERO, BigDecimal::add));
            }
            totalReceivedAmount = totalReceivedAmount.add(deviceDailyBillPo.getPaidAmount());
        }
        totalRevenueAmount = totalRevenueAmount.add(profitLossRecordMapper.selectList(Wrappers.lambdaQuery(ProfitLossRecordPo.class).eq(ProfitLossRecordPo::getContractId, contractId).eq(ProfitLossRecordPo::getBusinessBillType, BusinessBillTypeEnum.RECEIVABLE_ADJUSTMENT)).stream().map(ProfitLossRecordPo::getPaidAmount).reduce(BigDecimal.ZERO, BigDecimal::add));

        receivableInventoryPo.setTotalRevenueAmount(totalRevenueAmount);
        receivableInventoryPo.setTotalReceivedAmount(totalReceivedAmount);
        //应收总额=总营收-总已收
        receivableInventoryPo.setTotalReceivableAmount(receivableInventoryPo.getTotalRevenueAmount().subtract(receivableInventoryPo.getTotalReceivedAmount()));
        calculateReceivableInventoryPeriodAmount(receivableInventoryPo, deviceRentalRecordPoList);
        receivableInventoryPo.setUpdateTime(LocalDateTime.now());
        receivableInventoryPo.setUpdaterName("刷新账龄信息");
        return receivableInventoryMapper.updateById(receivableInventoryPo) > 0;
    }

    private void setUserInfoContextInfo(ReceivablesQueryDto receivablesQueryDto) {
        ContextUserInfo userInfo = UserContextUtil.getCurrentUser();
        if (Objects.isNull(userInfo) || Objects.isNull(userInfo.getId()) || Objects.isNull(userInfo.getRoles())) {
            throw new HMYRuntimeException(506000000);
        }
        receivablesQueryDto.setDeptIds(new ArrayList<>(userInfo.getDeptIds()));
        receivablesQueryDto.setRoles(userInfo.getRoles());
        //如果前端没传，并且当前用户为销售经理，则取当前登录用户的id作为销售经理id
        if (receivablesQueryDto.getRoles().contains(RoleEnum.SALE_MANAGER.getRoleType())) {
            receivablesQueryDto.setRoleSaleManagerId(userInfo.getId());
        }
    }


}




